/**
	This file is part of Fighting Field.

	Fighting Field is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Fighting Field is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Fighting Field.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "GuiManager.h"

namespace FF
{
	GuiManager::GuiManager(const Ogre::String& name, Ogre::RenderWindow* window, OIS::Mouse* mouse, GuiListener* listener) :
		mName(name), mWindow(window), mMouse(mouse), mWidgetDeathRow(), mListener(listener), mWidgetPadding(8),
		mWidgetSpacing(2), mTrayPadding(0), mTrayDrag(false), mExpandedMenu(0), mDialog(0), mOk(0), mYes(0),
		mNo(0), mCursorWasVisible(false), mFpsLabel(0), mStatsPanel(0), mLogo(0), mLoadBar(0),
		mGroupInitProportion(0.0f), mGroupLoadProportion(0.0f), mLoadInc(0.0f)
	{
		Ogre::OverlayManager& om = Ogre::OverlayManager::getSingleton();

		Ogre::String nameBase = mName + "/";
		std::replace(nameBase.begin(), nameBase.end(), ' ', '_');

		// create overlay layers for everything

		mBackdropLayer = om.create(nameBase + "BackdropLayer");
		mTraysLayer = om.create(nameBase + "WidgetsLayer");
		mPriorityLayer = om.create(nameBase + "PriorityLayer");
		mCursorLayer = om.create(nameBase + "CursorLayer");
		mBackdropLayer->setZOrder(100);
		mTraysLayer->setZOrder(200);
		mPriorityLayer->setZOrder(300);
		mCursorLayer->setZOrder(400);

		// make backdrop overlay containers
		mBackdrop = (Ogre::OverlayContainer*)om.createOverlayElement("Panel", nameBase + "Backdrop");
		mBackdropLayer->add2D(mBackdrop);
		mCursor = NULL;
		mDialogShade = NULL;

		memset(mTrays, NULL, sizeof(Ogre::OverlayContainer*)*10);
	}

	GuiManager::~GuiManager()
	{
		Ogre::OverlayManager& om = Ogre::OverlayManager::getSingleton();

		DestroyAllWidgets();

		for (unsigned int i = 0; i < mWidgetDeathRow.size(); i++)   // delete widgets queued for destruction
		{
			delete mWidgetDeathRow[i];
		}
		mWidgetDeathRow.clear();

		om.destroy(mBackdropLayer);
		om.destroy(mTraysLayer);
		om.destroy(mPriorityLayer);
		om.destroy(mCursorLayer);

		CloseDialog();
		HideLoadingBar();

		Widget::NukeOverlayElement(mBackdrop);
		Widget::NukeOverlayElement(mCursor);
		Widget::NukeOverlayElement(mDialogShade);

		for (unsigned int i = 0; i < 10; i++)
		{
			Widget::NukeOverlayElement(mTrays[i]);
		}
	}

	void GuiManager::InitCursor(const Ogre::String& templateName)
	{
		if (mCursor)
		{
			mCursorLayer->remove2D(mCursor);
			Widget::NukeOverlayElement(mCursor);
		}
		mCursor = (Ogre::OverlayContainer*)Ogre::OverlayManager::getSingleton().createOverlayElementFromTemplate(templateName, "Panel", mName + "/Cursor");
		mCursorLayer->add2D(mCursor);
		ShowCursor();
	}

	void GuiManager::InitDialogShade(const Ogre::String& materialName)
	{
		if (!mDialogShade)
		{
			mDialogShade = (Ogre::OverlayContainer*)Ogre::OverlayManager::getSingleton().createOverlayElement("Panel", mName + "/DialogShade");
			mDialogShade->hide();
			mPriorityLayer->add2D(mDialogShade);
		}
		mDialogShade->setMaterialName(materialName);
	}

	void GuiManager::InitTrays(const Ogre::String& templateName)
	{
		if (*mTrays)
		{
			DestroyAllWidgets();

			for (unsigned int i = 0; i < 10; i++)
			{
				Widget::NukeOverlayElement(mTrays[i]);
			}
		}

		Ogre::OverlayManager& om = Ogre::OverlayManager::getSingleton();

		Ogre::String trayNames[] =
		{ "TopLeft", "Top", "TopRight", "Left", "Center", "Right", "BottomLeft", "Bottom", "BottomRight" };

		for (unsigned int i = 0; i < 9; i++)    // make the real trays
		{
			mTrays[i] = (Ogre::OverlayContainer*)om.createOverlayElementFromTemplate
				(templateName, "BorderPanel", mName + "/" + trayNames[i] + "Tray");
			mTraysLayer->add2D(mTrays[i]);

			mTrayWidgetAlign[i] = Ogre::GHA_CENTER;

			// align trays based on location
			if (i == TrayLocation::Top || i == TrayLocation::Center || i == TrayLocation::Bottom) mTrays[i]->setHorizontalAlignment(Ogre::GHA_CENTER);
			if (i == TrayLocation::Left || i == TrayLocation::Center || i == TrayLocation::Right) mTrays[i]->setVerticalAlignment(Ogre::GVA_CENTER);
			if (i == TrayLocation::TopRight || i == TrayLocation::Right || i == TrayLocation::BottomRight) mTrays[i]->setHorizontalAlignment(Ogre::GHA_RIGHT);
			if (i == TrayLocation::BottomLeft || i == TrayLocation::Bottom || i == TrayLocation::BottomRight) mTrays[i]->setVerticalAlignment(Ogre::GVA_BOTTOM);
		}

		// create the null tray for free-floating widgets
		mTrays[9] = (Ogre::OverlayContainer*)om.createOverlayElement("Panel", mName + "/NullTray");
		mTrayWidgetAlign[9] = Ogre::GHA_LEFT;
		mTraysLayer->add2D(mTrays[9]);

		AdjustTrays();
		ShowTrays();
	}

	Ogre::Ray GuiManager::ScreenToScene(Ogre::Camera* cam, const Ogre::Vector2& pt)
	{
		return cam->getCameraToViewportRay(pt.x, pt.y);
	}

	Ogre::Vector2 GuiManager::SceneToScreen(Ogre::Camera* cam, const Ogre::Vector3& pt)
	{
		Ogre::Vector3 result = cam->getProjectionMatrix() * cam->getViewMatrix() * pt;
		return Ogre::Vector2((result.x + 1) / 2, -(result.y + 1) / 2);
	}

	void GuiManager::SetListener(GuiListener* listener)
	{
		mListener = listener;
	}

	GuiListener* GuiManager::GetListener()
	{
		return mListener;
	}

	void GuiManager::ShowAll()
	{
		ShowBackdrop();
		ShowTrays();
		ShowCursor();
	}

	void GuiManager::HideAll()
	{
		HideBackdrop();
		HideTrays();
		HideCursor();
	}

	void GuiManager::ShowBackdrop(const Ogre::String& materialName)
	{
		if (materialName != Ogre::StringUtil::BLANK) mBackdrop->setMaterialName(materialName);
			mBackdropLayer->show();
	}

	void GuiManager::HideBackdrop()
	{
		mBackdropLayer->hide();
	}

	void GuiManager::ShowCursor(const Ogre::String& materialName)
	{
		if (materialName != Ogre::StringUtil::BLANK) GetCursorImage()->setMaterialName(materialName);

		if (!mCursorLayer->isVisible())
		{
			mCursorLayer->show();
			RefreshCursor();
		}
	}

	void GuiManager::HideCursor()
	{
		mCursorLayer->hide();

		// give widgets a chance to reset in case they're in the middle of something
		for (unsigned int i = 0; i < 10; i++)
		{
			for (unsigned int j = 0; j < mWidgets[i].size(); j++)
			{
				mWidgets[i][j]->_OnFocusLost();
			}
		}

		SetExpandedMenu(0);
	}

	void GuiManager::RefreshCursor()
	{
#if OGRE_NO_VIEWPORT_ORIENTATIONMODE == 0
		// TODO:
		// the position should be based on the orientation, for now simply return
		return;
#endif
		mCursor->setPosition((Ogre::Real)mMouse->getMouseState().X.abs, (Ogre::Real)mMouse->getMouseState().Y.abs);
	}

	void GuiManager::ShowTrays()
	{
		mTraysLayer->show();
		mPriorityLayer->show();
	}

	void GuiManager::HideTrays()
	{
		mTraysLayer->hide();
		mPriorityLayer->hide();

		// give widgets a chance to reset in case they're in the middle of something
		for (unsigned int i = 0; i < 10; i++)
		{
			for (unsigned int j = 0; j < mWidgets[i].size(); j++)
			{
				mWidgets[i][j]->_OnFocusLost();
			}
		}

		SetExpandedMenu(0);
	}

	void GuiManager::SetTrayWidgetAlignment(TrayLocation::Val trayLoc, Ogre::GuiHorizontalAlignment gha)
	{
		mTrayWidgetAlign[trayLoc] = gha;

		for (unsigned int i = 0; i < mWidgets[trayLoc].size(); i++)
		{
			mWidgets[trayLoc][i]->GetOverlayElement()->setHorizontalAlignment(gha);
		}
	}

	void GuiManager::SetWidgetPadding(Ogre::Real padding)
	{
		mWidgetPadding = (Ogre::Real)std::max<int>((int)padding, 0);
		AdjustTrays();
	}

	void GuiManager::SetWidgetSpacing(Ogre::Real spacing)
	{
		mWidgetSpacing = (Ogre::Real)std::max<int>((int)spacing, 0);
		AdjustTrays();
	}
	void GuiManager::SetTrayPadding(Ogre::Real padding)
	{
		mTrayPadding = (Ogre::Real)std::max<int>((int)padding, 0);
		AdjustTrays();
	}

	void GuiManager::AdjustTrays()
	{
		for (unsigned int i = 0; i < 9; i++)   // resizes and hides trays if necessary
		{
			Ogre::Real trayWidth = 0;
			Ogre::Real trayHeight = mWidgetPadding;
			std::vector<Ogre::OverlayElement*> labelsAndSeps;

			if (mWidgets[i].empty())   // hide tray if empty
			{
				mTrays[i]->hide();
				continue;
			}
			else mTrays[i]->show();

			// arrange widgets and calculate final tray size and position
			for (unsigned int j = 0; j < mWidgets[i].size(); j++)
			{
				Ogre::OverlayElement* e = mWidgets[i][j]->GetOverlayElement();

				if (j != 0) trayHeight += mWidgetSpacing;   // don't space first widget

				e->setVerticalAlignment(Ogre::GVA_TOP);
				e->setTop(trayHeight);

				switch (e->getHorizontalAlignment())
				{
				case Ogre::GHA_LEFT:
					e->setLeft(mWidgetPadding);
					break;
				case Ogre::GHA_RIGHT:
					e->setLeft(-(e->getWidth() + mWidgetPadding));
					break;
				default:
					e->setLeft(-(e->getWidth() / 2));
				}

				// prevents some weird texture filtering problems (just some)
				e->setPosition((Ogre::Real)(int)e->getLeft(), (Ogre::Real)(int)e->getTop());
				e->setDimensions((Ogre::Real)(int)e->getWidth(), (Ogre::Real)(int)e->getHeight());

				trayHeight += e->getHeight();

				Label* l = dynamic_cast<Label*>(mWidgets[i][j]);
				if (l && l->_IsFitToTray())
				{
					labelsAndSeps.push_back(e);
					continue;
				}
				Separator* s = dynamic_cast<Separator*>(mWidgets[i][j]);
				if (s && s->_IsFitToTray()) 
				{
					labelsAndSeps.push_back(e);
					continue;
				}

				if (e->getWidth() > trayWidth) trayWidth = e->getWidth();
			}

			// add paddings and resize trays
			mTrays[i]->setWidth(trayWidth + 2 * mWidgetPadding);
			mTrays[i]->setHeight(trayHeight + mWidgetPadding);

			for (unsigned int j = 0; j < labelsAndSeps.size(); j++)
			{
				labelsAndSeps[j]->setWidth((Ogre::Real)(int)trayWidth);
				labelsAndSeps[j]->setLeft(-(Ogre::Real)(int)(trayWidth / 2));
			}
		}

		for (unsigned int i = 0; i < 9; i++)    // snap trays to anchors
		{
			if (i == TrayLocation::TopLeft || i == TrayLocation::Left || i == TrayLocation::BottomLeft)
				mTrays[i]->setLeft(mTrayPadding);
			if (i == TrayLocation::Top || i == TrayLocation::Center || i == TrayLocation::Bottom)
				mTrays[i]->setLeft(-mTrays[i]->getWidth() / 2);
			if (i == TrayLocation::TopRight || i == TrayLocation::Right || i == TrayLocation::BottomRight)
				mTrays[i]->setLeft(-(mTrays[i]->getWidth() + mTrayPadding));

			if (i == TrayLocation::TopLeft || i == TrayLocation::Top || i == TrayLocation::TopRight)
				mTrays[i]->setTop(mTrayPadding);
			if (i == TrayLocation::Left || i == TrayLocation::Center || i == TrayLocation::Right)
				mTrays[i]->setTop(-mTrays[i]->getHeight() / 2);
			if (i == TrayLocation::BottomLeft || i == TrayLocation::Bottom || i == TrayLocation::BottomRight)
				mTrays[i]->setTop(-mTrays[i]->getHeight() - mTrayPadding);

			// prevents some weird texture filtering problems (just some)
			mTrays[i]->setPosition((Ogre::Real)(int)mTrays[i]->getLeft(), (Ogre::Real)(int)mTrays[i]->getTop());
			mTrays[i]->setDimensions((Ogre::Real)(int)mTrays[i]->getWidth(), (Ogre::Real)(int)mTrays[i]->getHeight());
		}
	}

	Ogre::Ray GuiManager::GetCursorRay(Ogre::Camera* cam)
	{
		return ScreenToScene(cam, Ogre::Vector2(mCursor->_getLeft(), mCursor->_getTop()));
	}

	Button* GuiManager::CreateButton(TrayLocation::Val trayLoc, const Ogre::String& name, const Button::Style& style, const Ogre::String& caption, Ogre::Real width)
	{
		Button* b = new Button(name, style, caption, width);
		MoveWidgetToTray(b, trayLoc);
		b->_AssignListener(mListener);
		return b;
	}

	TextBox* GuiManager::CreateTextBox(TrayLocation::Val trayLoc, const Ogre::String& name, const TextBox::Style& style, const Ogre::DisplayString& caption,
		Ogre::Real width, Ogre::Real height)
	{
		TextBox* tb = new TextBox(name, style, caption, width, height);
		MoveWidgetToTray(tb, trayLoc);
		tb->_AssignListener(mListener);
		return tb;
	}

	SelectMenu* GuiManager::CreateThickSelectMenu(TrayLocation::Val trayLoc, const Ogre::String& name, const SelectMenu::Style& style, const Ogre::DisplayString& caption,
		Ogre::Real width, unsigned int maxItemsShown, const Ogre::StringVector& items)
	{
		SelectMenu* sm = new SelectMenu(name, style, caption, width, 0, maxItemsShown);
		MoveWidgetToTray(sm, trayLoc);
		sm->_AssignListener(mListener);
		if (!items.empty()) sm->SetItems(items);
		return sm;
	}

	SelectMenu* GuiManager::CreateLongSelectMenu(TrayLocation::Val trayLoc, const Ogre::String& name, const SelectMenu::Style& style, const Ogre::DisplayString& caption,
		Ogre::Real width, Ogre::Real boxWidth, unsigned int maxItemsShown, const Ogre::StringVector& items)
	{
		SelectMenu* sm = new SelectMenu(name, style, caption, width, boxWidth, maxItemsShown);
		MoveWidgetToTray(sm, trayLoc);
		sm->_AssignListener(mListener);
		if (!items.empty()) sm->SetItems(items);
		return sm;
	}

	SelectMenu* GuiManager::CreateLongSelectMenu(TrayLocation::Val trayLoc, const Ogre::String& name, const SelectMenu::Style& style, const Ogre::DisplayString& caption,
		Ogre::Real boxWidth, unsigned int maxItemsShown, const Ogre::StringVector& items)
	{
		return CreateLongSelectMenu(trayLoc, name, style, caption, 0, boxWidth, maxItemsShown, items);
	}

	Label* GuiManager::CreateLabel(TrayLocation::Val trayLoc, const Ogre::String& name, const Label::Style& style, const Ogre::DisplayString& caption, Ogre::Real width)
	{
		Label* l = new Label(name, style, caption, width);
		MoveWidgetToTray(l, trayLoc);
		l->_AssignListener(mListener);
		return l;
	}

	Separator* GuiManager::CreateSeparator(TrayLocation::Val trayLoc, const Ogre::String& name, const Separator::Style& style, Ogre::Real width)
	{
		Separator* s = new Separator(name, style, width);
		MoveWidgetToTray(s, trayLoc);
		return s;
	}

	Slider* GuiManager::CreateThickSlider(TrayLocation::Val trayLoc, const Ogre::String& name, const Slider::Style& style, const Ogre::DisplayString& caption,
		Ogre::Real width, Ogre::Real valueBoxWidth, Ogre::Real minValue, Ogre::Real maxValue, unsigned int snaps)
	{
		Slider* s = new Slider(name, style, caption, width, 0, valueBoxWidth, minValue, maxValue, snaps);
		MoveWidgetToTray(s, trayLoc);
		s->_AssignListener(mListener);
		return s;
	}

	Slider* GuiManager::CreateLongSlider(TrayLocation::Val trayLoc, const Ogre::String& name, const Slider::Style& style, const Ogre::DisplayString& caption, Ogre::Real width,
		Ogre::Real trackWidth, Ogre::Real valueBoxWidth, Ogre::Real minValue, Ogre::Real maxValue, unsigned int snaps)
	{
		if (trackWidth <= 0) trackWidth = 1;
		Slider* s = new Slider(name, style, caption, width, trackWidth, valueBoxWidth, minValue, maxValue, snaps);
		MoveWidgetToTray(s, trayLoc);
		s->_AssignListener(mListener);
		return s;
	}

	Slider* GuiManager::CreateLongSlider(TrayLocation::Val trayLoc, const Ogre::String& name, const Slider::Style& style, const Ogre::DisplayString& caption,
		Ogre::Real trackWidth, Ogre::Real valueBoxWidth, Ogre::Real minValue, Ogre::Real maxValue, unsigned int snaps)
	{
		return CreateLongSlider(trayLoc, name, style, caption, 0, trackWidth, valueBoxWidth, minValue, maxValue, snaps);
	}

	ParamsPanel* GuiManager::CreateParamsPanel(TrayLocation::Val trayLoc, const Ogre::String& name, const ParamsPanel::Style& style, Ogre::Real width, unsigned int lines)
	{
		ParamsPanel* pp = new ParamsPanel(name, style, width, lines);
		MoveWidgetToTray(pp, trayLoc);
		return pp;
	}

	ParamsPanel* GuiManager::CreateParamsPanel(TrayLocation::Val trayLoc, const Ogre::String& name, const ParamsPanel::Style& style, Ogre::Real width,
		const Ogre::StringVector& paramNames)
	{
		ParamsPanel* pp = new ParamsPanel(name, style, width, paramNames.size());
		pp->SetAllParamNames(paramNames);
		MoveWidgetToTray(pp, trayLoc);
		return pp;
	}

	CheckBox* GuiManager::CreateCheckBox(TrayLocation::Val trayLoc, const Ogre::String& name, const CheckBox::Style& style, const Ogre::DisplayString& caption,
		Ogre::Real width)
	{
		CheckBox* cb = new CheckBox(name, style, caption, width);
		MoveWidgetToTray(cb, trayLoc);
		cb->_AssignListener(mListener);
		return cb;
	}

	DecorWidget* GuiManager::CreateDecorWidget(TrayLocation::Val trayLoc, const Ogre::String& name, const Ogre::String& templateName)
	{
		DecorWidget* dw = new DecorWidget(name, templateName);
		MoveWidgetToTray(dw, trayLoc);
		return dw;
	}

	ProgressBar* GuiManager::CreateProgressBar(TrayLocation::Val trayLoc, const Ogre::String& name, const ProgressBar::Style& style, const Ogre::DisplayString& caption,
		Ogre::Real width, Ogre::Real commentBoxWidth)
	{
		ProgressBar* pb = new ProgressBar(name, style, caption, width, commentBoxWidth);
		MoveWidgetToTray(pb, trayLoc);
		return pb;
	}

	void GuiManager::ShowFrameStats(TrayLocation::Val trayLoc, int place)
	{
		if (!AreFrameStatsVisible())
		{
			Ogre::StringVector stats;
			stats.push_back("Average FPS");
			stats.push_back("Best FPS");
			stats.push_back("Worst FPS");
			stats.push_back("Triangles");
			stats.push_back("Batches");

			mFpsLabel = CreateLabel(TrayLocation::None, mName + "/FpsLabel", "FPS:", 180);
			mFpsLabel->_AssignListener(this);
			mStatsPanel = CreateParamsPanel(TrayLocation::None, mName + "/StatsPanel", 180, stats);
		}

		MoveWidgetToTray(mFpsLabel, trayLoc, place);
		MoveWidgetToTray(mStatsPanel, trayLoc, LocateWidgetInTray(mFpsLabel) + 1);
	}

	void GuiManager::HideFrameStats()
	{
		if (AreFrameStatsVisible())
		{
			DestroyWidget(mFpsLabel);
			DestroyWidget(mStatsPanel);
			mFpsLabel = 0;
			mStatsPanel = 0;
		}
	}

	bool GuiManager::AreFrameStatsVisible()
	{
		return mFpsLabel != 0;
	}

	void GuiManager::ToggleAdvancedFrameStats()
	{
		if (mFpsLabel) OnLabelHit(mFpsLabel);
	}

	void GuiManager::ShowLogo(const Ogre::String& templateName, TrayLocation::Val trayLoc, int place)
	{
		if (!IsLogoVisible()) mLogo = CreateDecorWidget(TrayLocation::None, mName + "/Logo", templateName);
		MoveWidgetToTray(mLogo, trayLoc, place);
	}

	void GuiManager::HideLogo()
	{
		if (IsLogoVisible())
		{
			DestroyWidget(mLogo);
			mLogo = 0;
		}
	}

	bool GuiManager::IsLogoVisible()
	{
		return mLogo != 0;
	}

	void GuiManager::ShowLoadingBar(unsigned int numGroupsInit, unsigned int numGroupsLoad,
		Ogre::Real initProportion)
	{
		if (mDialog) CloseDialog();
		if (mLoadBar) HideLoadingBar();

		mLoadBar = new ProgressBar(mName + "/LoadingBar", "Loading...", 400, 308);
		Ogre::OverlayElement* e = mLoadBar->GetOverlayElement();
		mDialogShade->addChild(e);
		e->setVerticalAlignment(Ogre::GVA_CENTER);
		e->setLeft(-(e->getWidth() / 2));
		e->setTop(-(e->getHeight() / 2));

		Ogre::ResourceGroupManager::getSingleton().addResourceGroupListener(this);
		mCursorWasVisible = IsCursorVisible();
		HideCursor();
		mDialogShade->show();

		// calculate the proportion of job required to init/load one group

		if (numGroupsInit == 0 && numGroupsLoad != 0)
		{
			mGroupInitProportion = 0;
			mGroupLoadProportion = 1;
		}
		else if (numGroupsLoad == 0 && numGroupsInit != 0)
		{
			mGroupLoadProportion = 0;
			if (numGroupsInit != 0) mGroupInitProportion = 1;
		}
		else if (numGroupsInit == 0 && numGroupsLoad == 0)
		{
			mGroupInitProportion = 0;
			mGroupLoadProportion = 0;
		}
		else
		{
			mGroupInitProportion = initProportion / numGroupsInit;
			mGroupLoadProportion = (1 - initProportion) / numGroupsLoad;
		}
	}

	void GuiManager::HideLoadingBar()
	{
		if (mLoadBar)
		{
			mLoadBar->Cleanup();
			delete mLoadBar;
			mLoadBar = 0;

			Ogre::ResourceGroupManager::getSingleton().removeResourceGroupListener(this);
			if (mCursorWasVisible) ShowCursor();
			mDialogShade->hide();
		}
	}

	bool GuiManager::IsLoadingBarVisible()
	{
		return mLoadBar != 0;
	}

	void GuiManager::ShowOkDialog(const Ogre::DisplayString& caption, const Ogre::DisplayString& message)
	{
		if (mLoadBar) HideLoadingBar();

		Ogre::OverlayElement* e;

		if (mDialog)
		{
			mDialog->SetCaption(caption);
			mDialog->SetText(message);

			if (mOk) return;
			else
			{
				mYes->Cleanup();
				mNo->Cleanup();
				delete mYes;
				delete mNo;
				mYes = 0;
				mNo = 0;
			}
		}
		else
		{
			// give widgets a chance to reset in case they're in the middle of something
			for (unsigned int i = 0; i < 10; i++)
			{
				for (unsigned int j = 0; j < mWidgets[i].size(); j++)
				{
					mWidgets[i][j]->_OnFocusLost();
				}
			}

			mDialogShade->show();

			mDialog = new TextBox(mName + "/DialogBox", caption, 300, 208);
			mDialog->SetText(message);
			e = mDialog->GetOverlayElement();
			mDialogShade->addChild(e);
			e->setVerticalAlignment(Ogre::GVA_CENTER);
			e->setLeft(-(e->getWidth() / 2));
			e->setTop(-(e->getHeight() / 2));

			mCursorWasVisible = IsCursorVisible();
			ShowCursor();
		}

		mOk = new Button(mName + "/OkButton", "OK", 60);
		mOk->_AssignListener(this);
		e = mOk->GetOverlayElement();
		mDialogShade->addChild(e);
		e->setVerticalAlignment(Ogre::GVA_CENTER);
		e->setLeft(-(e->getWidth() / 2));
		e->setTop(mDialog->GetOverlayElement()->getTop() + mDialog->GetOverlayElement()->getHeight() + 5);
	}

	void GuiManager::ShowYesNoDialog(const Ogre::DisplayString& caption, const Ogre::DisplayString& question)
	{
		if (mLoadBar) HideLoadingBar();

		Ogre::OverlayElement* e;

		if (mDialog)
		{
			mDialog->SetCaption(caption);
			mDialog->SetText(question);

			if (mOk)
			{
				mOk->Cleanup();
				delete mOk;
				mOk = 0;
			}
			else return;
		}
		else
		{
			// give widgets a chance to reset in case they're in the middle of something
			for (unsigned int i = 0; i < 10; i++)
			{
				for (unsigned int j = 0; j < mWidgets[i].size(); j++)
				{
					mWidgets[i][j]->_OnFocusLost();
				}
			}

			mDialogShade->show();

			mDialog = new TextBox(mName + "/DialogBox", caption, 300, 208);
			mDialog->SetText(question);
			e = mDialog->GetOverlayElement();
			mDialogShade->addChild(e);
			e->setVerticalAlignment(Ogre::GVA_CENTER);
			e->setLeft(-(e->getWidth() / 2));
			e->setTop(-(e->getHeight() / 2));

			mCursorWasVisible = IsCursorVisible();
			ShowCursor();
		}

		mYes = new Button(mName + "/YesButton", "Yes", 58);
		mYes->_AssignListener(this);
		e = mYes->GetOverlayElement();
		mDialogShade->addChild(e);
		e->setVerticalAlignment(Ogre::GVA_CENTER);
		e->setLeft(-(e->getWidth() + 2));
		e->setTop(mDialog->GetOverlayElement()->getTop() + mDialog->GetOverlayElement()->getHeight() + 5);

		mNo = new Button(mName + "/NoButton", "No", 50);
		mNo->_AssignListener(this);
		e = mNo->GetOverlayElement();
		mDialogShade->addChild(e);
		e->setVerticalAlignment(Ogre::GVA_CENTER);
		e->setLeft(3);
		e->setTop(mDialog->GetOverlayElement()->getTop() + mDialog->GetOverlayElement()->getHeight() + 5);
	}

	void GuiManager::CloseDialog()
	{
		if (mDialog)
		{
			if (mOk)
			{
				mOk->Cleanup();
				delete mOk;
				mOk = 0;
			}
			else
			{
				mYes->Cleanup();
				mNo->Cleanup();
				delete mYes;
				delete mNo;
				mYes = 0;
				mNo = 0;
			}

			mDialogShade->hide();
			mDialog->Cleanup();
			delete mDialog;
			mDialog = 0;

			if (!mCursorWasVisible) HideCursor();
		}
	}

	bool GuiManager::IsDialogVisible()
	{
		return mDialog != 0;
	}

	Widget* GuiManager::GetWidget(TrayLocation::Val trayLoc, unsigned int place)
	{
		if (place < mWidgets[trayLoc].size()) return mWidgets[trayLoc][place];
		return 0;
	}

	Widget* GuiManager::GetWidget(TrayLocation::Val trayLoc, const Ogre::String& name)
	{
		for (unsigned int i = 0; i < mWidgets[trayLoc].size(); i++)
		{
			if (mWidgets[trayLoc][i]->GetName() == name) return mWidgets[trayLoc][i];
		}
		return 0;
	}

	Widget* GuiManager::GetWidget(const Ogre::String& name)
	{
		for (unsigned int i = 0; i < 10; i++)
		{
			for (unsigned int j = 0; j < mWidgets[i].size(); j++)
			{
				if (mWidgets[i][j]->GetName() == name) return mWidgets[i][j];
			}
		}
		return 0;
	}

	unsigned int GuiManager::GetNumWidgets()
	{
		unsigned int total = 0;

		for (unsigned int i = 0; i < 10; i++)
		{
			total += mWidgets[i].size();
		}

		return total;
	}

	unsigned int GuiManager::GetNumWidgets(TrayLocation::Val trayLoc)
	{
		return mWidgets[trayLoc].size();
	}

	WidgetIterator GuiManager::GetWidgetIterator(TrayLocation::Val trayLoc)
	{
		return WidgetIterator(mWidgets[trayLoc].begin(), mWidgets[trayLoc].end());
	}

	int GuiManager::LocateWidgetInTray(Widget* widget)
	{
		for (unsigned int i = 0; i < mWidgets[widget->GetTrayLocation()].size(); i++)
		{
			if (mWidgets[widget->GetTrayLocation()][i] == widget) return i;
		}
		return -1;
	}

	void GuiManager::DestroyWidget(Widget* widget)
	{
		if (!widget) OGRE_EXCEPT(Ogre::Exception::ERR_ITEM_NOT_FOUND, "Widget does not exist.", "TrayManager::DestroyWidget");

		// in case special widgets are destroyed manually, set them to 0
		if (widget == mLogo) mLogo = 0;
		else if (widget == mStatsPanel) mStatsPanel = 0;
		else if (widget == mFpsLabel) mFpsLabel = 0;

		mTrays[widget->GetTrayLocation()]->removeChild(widget->GetName());

		WidgetList& wList = mWidgets[widget->GetTrayLocation()];
		wList.erase(std::find(wList.begin(), wList.end(), widget));
		if (widget == mExpandedMenu) SetExpandedMenu(0);

		widget->Cleanup();

		mWidgetDeathRow.push_back(widget);

		AdjustTrays();
	}

	void GuiManager::DestroyWidget(TrayLocation::Val trayLoc, unsigned int place)
	{
		DestroyWidget(GetWidget(trayLoc, place));
	}

	void GuiManager::DestroyWidget(TrayLocation::Val trayLoc, const Ogre::String& name)
	{
		DestroyWidget(GetWidget(trayLoc, name));
	}

	void GuiManager::DestroyWidget(const Ogre::String& name)
	{
		DestroyWidget(GetWidget(name));
	}

	void GuiManager::DestroyAllWidgetsInTray(TrayLocation::Val trayLoc)
	{
		while (!mWidgets[trayLoc].empty()) DestroyWidget(mWidgets[trayLoc][0]);
	}

	void GuiManager::DestroyAllWidgets()
	{
		for (unsigned int i = 0; i < 10; i++)  // destroy every widget in every tray (including null tray)
		{
			DestroyAllWidgetsInTray((TrayLocation::Val)i);
		}
	}

	void GuiManager::MoveWidgetToTray(Widget* widget, TrayLocation::Val trayLoc, int place)
	{
		if (!widget) OGRE_EXCEPT(Ogre::Exception::ERR_ITEM_NOT_FOUND, "Widget does not exist.", "TrayManager::MoveWidgetToTray");

		// remove widget from old tray
		WidgetList& wList = mWidgets[widget->GetTrayLocation()];
		WidgetList::iterator it = std::find(wList.begin(), wList.end(), widget);
		if (it != wList.end())
		{
			wList.erase(it);
			mTrays[widget->GetTrayLocation()]->removeChild(widget->GetName());
		}

		// insert widget into new tray at given position, or at the end if unspecified or invalid
		if (place == -1 || place > (int)mWidgets[trayLoc].size()) place = mWidgets[trayLoc].size();
		mWidgets[trayLoc].insert(mWidgets[trayLoc].begin() + place, widget);
		mTrays[trayLoc]->addChild(widget->GetOverlayElement());

		widget->GetOverlayElement()->setHorizontalAlignment(mTrayWidgetAlign[trayLoc]);

		// adjust trays if necessary
		if (widget->GetTrayLocation() != TrayLocation::None || trayLoc != TrayLocation::None) AdjustTrays();

		widget->_AssignToTray(trayLoc);
	}

	void GuiManager::MoveWidgetToTray(const Ogre::String& name, TrayLocation::Val trayLoc, unsigned int place)
	{
		MoveWidgetToTray(GetWidget(name), trayLoc, place);
	}

	void GuiManager::MoveWidgetToTray(TrayLocation::Val currentTrayLoc, const Ogre::String& name, TrayLocation::Val targetTrayLoc,
		int place)
	{
		MoveWidgetToTray(GetWidget(currentTrayLoc, name), targetTrayLoc, place);
	}

	void GuiManager::MoveWidgetToTray(TrayLocation::Val currentTrayLoc, unsigned int currentPlace, TrayLocation::Val targetTrayLoc,
		int targetPlace)
	{
		MoveWidgetToTray(GetWidget(currentTrayLoc, currentPlace), targetTrayLoc, targetPlace);
	}

	void GuiManager::RemoveWidgetFromTray(Widget* widget)
	{
		MoveWidgetToTray(widget, TrayLocation::None);
	}

	void GuiManager::RemoveWidgetFromTray(const Ogre::String& name)
	{
		RemoveWidgetFromTray(GetWidget(name));
	}

	void GuiManager::RemoveWidgetFromTray(TrayLocation::Val trayLoc, const Ogre::String& name)
	{
		RemoveWidgetFromTray(GetWidget(trayLoc, name));
	}

	void GuiManager::RemoveWidgetFromTray(TrayLocation::Val trayLoc, int place)
	{
		RemoveWidgetFromTray(GetWidget(trayLoc, place));
	}

	void GuiManager::ClearTray(TrayLocation::Val trayLoc)
	{
		if (trayLoc == TrayLocation::None) return;      // can't clear the null tray

		while (!mWidgets[trayLoc].empty())   // remove every widget from given tray
		{
			RemoveWidgetFromTray(mWidgets[trayLoc][0]);
		}
	}

	void GuiManager::ClearAllTrays()
	{
		for (unsigned int i = 0; i < 9; i++)
		{
			ClearTray((TrayLocation::Val)i);
		}
	}

	bool GuiManager::Update(const Ogre::Real& dt)
	{
		for (unsigned int i = 0; i < mWidgetDeathRow.size(); i++)
		{
			delete mWidgetDeathRow[i];
		}
		mWidgetDeathRow.clear();

		Ogre::RenderTarget::FrameStats stats = mWindow->getStatistics();

		if (AreFrameStatsVisible())
		{
			std::ostringstream oss;
			Ogre::String s;

			oss << "FPS: " << std::fixed << std::setprecision(1) << stats.lastFPS;
			s = oss.str();
			for (int i = s.length() - 5; i > 5; i -= 3) { s.insert(i, 1, ','); }
			mFpsLabel->SetCaption(s);

			if (mStatsPanel->GetOverlayElement()->isVisible())
			{
				Ogre::StringVector values;

				oss.str("");
				oss << std::fixed << std::setprecision(1) << stats.avgFPS;
				Ogre::String str = oss.str();
				for (int i = str.length() - 5; i > 0; i -= 3) { str.insert(i, 1, ','); }
				values.push_back(s);

				oss.str("");
				oss << std::fixed << std::setprecision(1) << stats.bestFPS;
				str = oss.str();
				for (int i = str.length() - 5; i > 0; i -= 3) { str.insert(i, 1, ','); }
				values.push_back(s);

				oss.str("");
				oss << std::fixed << std::setprecision(1) << stats.worstFPS;
				str = oss.str();
				for (int i = str.length() - 5; i > 0; i -= 3) { str.insert(i, 1, ','); }
				values.push_back(str);

				str = Ogre::StringConverter::toString(stats.triangleCount);
				for (int i = str.length() - 3; i > 0; i -= 3) { str.insert(i, 1, ','); }
				values.push_back(str);

				str = Ogre::StringConverter::toString(stats.batchCount);
				for (int i = str.length() - 3; i > 0; i -= 3) { str.insert(i, 1, ','); }
				values.push_back(str);

				mStatsPanel->SetAllParamValues(values);
			}
		}

		return true;
	}

	void GuiManager::resourceGroupScriptingStarted(const Ogre::String& groupName, size_t scriptCount)
	{
		mLoadInc = mGroupInitProportion / scriptCount;
		mLoadBar->SetCaption("Parsing...");
		mWindow->update();
	}

	void GuiManager::scriptParseStarted(const Ogre::String& scriptName, bool& skipThisScript)
	{
		mLoadBar->SetComment(scriptName);
		mWindow->update();
	}

	void GuiManager::scriptParseEnded(const Ogre::String& scriptName, bool skipped)
	{
		mLoadBar->SetProgress(mLoadBar->GetProgress() + mLoadInc);
		mWindow->update();
	}

	void GuiManager::resourceGroupLoadStarted(const Ogre::String& groupName, size_t resourceCount)
	{
		mLoadInc = mGroupLoadProportion / resourceCount;
		mLoadBar->SetCaption("Loading...");
		mWindow->update();
	}

	void GuiManager::resourceLoadStarted(const Ogre::ResourcePtr& resource)
	{
		mLoadBar->SetComment(resource->getName());
		mWindow->update();
	}

	void GuiManager::resourceLoadEnded()
	{
		mLoadBar->SetProgress(mLoadBar->GetProgress() + mLoadInc);
		mWindow->update();
	}

	void GuiManager::worldGeometryStageStarted(const Ogre::String& description)
	{
		mLoadBar->SetComment(description);
		mWindow->update();
	}

	void GuiManager::worldGeometryStageEnded()
	{
		mLoadBar->SetProgress(mLoadBar->GetProgress() + mLoadInc);
		mWindow->update();
	}

	void GuiManager::OnLabelHit(Label* label)
	{
		if (mStatsPanel->GetOverlayElement()->isVisible())
		{
			mStatsPanel->GetOverlayElement()->hide();
			mFpsLabel->GetOverlayElement()->setWidth(150);
			RemoveWidgetFromTray(mStatsPanel);
		}
		else
		{
			mStatsPanel->GetOverlayElement()->show();
			mFpsLabel->GetOverlayElement()->setWidth(180);
			MoveWidgetToTray(mStatsPanel, mFpsLabel->GetTrayLocation(), LocateWidgetInTray(mFpsLabel) + 1);
		}
	}

	void GuiManager::OnButtonHit(Button* button)
	{
		if (mListener)
		{
			if (button == mOk) mListener->OnOkDialogClosed(mDialog->GetText());
			else mListener->OnYesNoDialogClosed(mDialog->GetText(), button == mYes);
		}
		CloseDialog();
	}

	bool GuiManager::OnMousePressed(const OIS::MouseEvent& evt, OIS::MouseButtonID id)
	{
		// only process left button when stuff is visible
		if (!mCursorLayer->isVisible() || id != OIS::MB_Left) return false;
		Ogre::Vector2 cursorPos(mCursor->getLeft(), mCursor->getTop());

		mTrayDrag = false;

		if (mExpandedMenu)   // only check top priority widget until it passes on
		{
			mExpandedMenu->_OnCursorPressed(cursorPos);
			if (!mExpandedMenu->IsExpanded()) SetExpandedMenu(0);
			return true;
		}

		if (mDialog)   // only check top priority widget until it passes on
		{
			mDialog->_OnCursorPressed(cursorPos);
			if (mOk) mOk->_OnCursorPressed(cursorPos);
			else
			{
				mYes->_OnCursorPressed(cursorPos);
				mNo->_OnCursorPressed(cursorPos);
			}
			return true;
		}

		for (unsigned int i = 0; i < 9; i++)   // check if mouse is over a non-null tray
		{
			if (mTrays[i]->isVisible() && Widget::IsCursorOver(mTrays[i], cursorPos, 2))
			{
				mTrayDrag = true;   // initiate a drag that originates in a tray
				break;
			}
		}

		for (unsigned int i = 0; i < mWidgets[9].size(); i++)  // check if mouse is over a non-null tray's widgets
		{
			if (mWidgets[9][i]->GetOverlayElement()->isVisible() &&
				Widget::IsCursorOver(mWidgets[9][i]->GetOverlayElement(), cursorPos))
			{
				mTrayDrag = true;   // initiate a drag that originates in a tray
				break;
			}
		}

		if (!mTrayDrag) return false;   // don't process if mouse press is not in tray

		for (unsigned int i = 0; i < 10; i++)
		{
			if (!mTrays[i]->isVisible()) continue;

			for (unsigned int j = 0; j < mWidgets[i].size(); j++)
			{
				Widget* w = mWidgets[i][j];
				if (!w->GetOverlayElement()->isVisible()) continue;
				w->_OnCursorPressed(cursorPos);    // send event to widget

				SelectMenu* m = dynamic_cast<SelectMenu*>(w);
				if (m && m->IsExpanded())       // a menu has begun a top priority session
				{
					SetExpandedMenu(m);
					return true;
				}
			}
		}

		return true;   // a tray click is not to be handled by another party
	}

	bool GuiManager::OnMouseReleased(const OIS::MouseEvent& evt, OIS::MouseButtonID id)
	{
		// only process left button when stuff is visible
		if (!mCursorLayer->isVisible() || id != OIS::MB_Left) return false;
		Ogre::Vector2 cursorPos(mCursor->getLeft(), mCursor->getTop());

		if (mExpandedMenu)   // only check top priority widget until it passes on
		{
			mExpandedMenu->_OnCursorReleased(cursorPos);
			return true;
		}

		if (mDialog)   // only check top priority widget until it passes on
		{
			mDialog->_OnCursorReleased(cursorPos);
			if (mOk) mOk->_OnCursorReleased(cursorPos);
			else
			{
				mYes->_OnCursorReleased(cursorPos);
				// very important to check if second button still exists, because first button could've closed the popup
				if (mNo) mNo->_OnCursorReleased(cursorPos); 
			}
			return true;
		}

		if (!mTrayDrag) return false;    // this click did not originate in a tray, so don't process

		Widget* w;

		for (unsigned int i = 0; i < 10; i++)
		{
			if (!mTrays[i]->isVisible()) continue;

			for (unsigned int j = 0; j < mWidgets[i].size(); j++)
			{
				w = mWidgets[i][j];
				if (!w->GetOverlayElement()->isVisible()) continue;
				w->_OnCursorReleased(cursorPos);    // send event to widget
			}
		}

		mTrayDrag = false;   // stop this drag
		return true;         // this click did originate in this tray, so don't pass it on
	}

	bool GuiManager::OnMouseMoved(const OIS::MouseEvent& evt)
	{
		if (!mCursorLayer->isVisible()) return false;   // don't process if cursor layer is invisible

		Ogre::Vector2 cursorPos((Ogre::Real)evt.state.X.abs, (Ogre::Real)evt.state.Y.abs);
		mCursor->setPosition(cursorPos.x, cursorPos.y);

		if (mExpandedMenu)   // only check top priority widget until it passes on
		{
			mExpandedMenu->_OnCursorMoved(cursorPos);
			return true;
		}

		if (mDialog)   // only check top priority widget until it passes on
		{
			mDialog->_OnCursorMoved(cursorPos);
			if (mOk) mOk->_OnCursorMoved(cursorPos);
			else
			{
				mYes->_OnCursorMoved(cursorPos);
				mNo->_OnCursorMoved(cursorPos);
			}
			return true;
		}

		Widget* w;

		for (unsigned int i = 0; i < 10; i++)
		{
			if (!mTrays[i]->isVisible()) continue;

			for (unsigned int j = 0; j < mWidgets[i].size(); j++)
			{
				w = mWidgets[i][j];
				if (!w->GetOverlayElement()->isVisible()) continue;
				w->_OnCursorMoved(cursorPos);    // send event to widget
			}
		}

		if (mTrayDrag) return true;  // don't pass this event on if we're in the middle of a drag
		return false;
	}

	void GuiManager::SetExpandedMenu(SelectMenu* m)
	{
		if (!mExpandedMenu && m)
		{
			Ogre::OverlayContainer* c = (Ogre::OverlayContainer*)m->GetOverlayElement();
			Ogre::OverlayContainer* eb = (Ogre::OverlayContainer*)c->getChild(m->GetName() + "/MenuExpandedBox");
			eb->_update();
			eb->setPosition
				((Ogre::Real)(unsigned int)(eb->_getDerivedLeft() * Ogre::OverlayManager::getSingleton().getViewportWidth()),
				(Ogre::Real)(unsigned int)(eb->_getDerivedTop() * Ogre::OverlayManager::getSingleton().getViewportHeight()));
			c->removeChild(eb->getName());
			mPriorityLayer->add2D(eb);
		}
		else if(mExpandedMenu && !m)
		{
			Ogre::OverlayContainer* eb = mPriorityLayer->getChild(mExpandedMenu->GetName() + "/MenuExpandedBox");
			mPriorityLayer->remove2D(eb);
			((Ogre::OverlayContainer*)mExpandedMenu->GetOverlayElement())->addChild(eb);
		}

		mExpandedMenu = m;
	}
}
